package fr.umlv.metadata;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;

import javax.swing.JOptionPane;

import org.alfresco.cmis.client.AlfrescoDocument;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Property;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
import org.apache.chemistry.opencmis.commons.enums.PropertyType;
import org.netbeans.spi.wizard.Summary;
import org.netbeans.spi.wizard.WizardException;
import org.netbeans.spi.wizard.WizardPage.WizardResultProducer;

import fr.umlv.authentication.SessionHandler;
import fr.umlv.utils.ISO8601DateFormat;


// TODO: Auto-generated Javadoc
/**
 * The Class MetadataResultProducer.
 */
public class MetadataResultProducer implements WizardResultProducer {

	/** The Constant TITLE_FIELD. */
	public static final String TITLE_FIELD = "title";
	
	/** The Constant AUTHOR_FIELD. */
	public static final String AUTHOR_FIELD = "author";
	
	/** The Constant DESCRIPTION_FIELD. */
	public static final String DESCRIPTION_FIELD = "description";
	
	/** The Constant CATEGORY_FIELD. */
	public static final String CATEGORY_FIELD = "category";

	/** The filename. */
	private List<String> filename;
	
	/** The extension map. */
	private Map<String, Map<String, Object>> extensionMap;
	
	/** The aspect utils. */
	private AspectUtils aspectUtils;

	/** The cmis object. */
	private CmisObject cmisObject;
	
	/** The doc. */
	private AlfrescoDocument doc;

	/** The bundle. */
	private ResourceBundle bundle = ResourceBundle.getBundle("fr.umlv.metadata.MetadataPage",SessionHandler.getLocale());

	/**
	 * Instantiates a new metadata result producer.
	 *
	 * @param extensions the extensions
	 * @param filename the filename
	 * @param aspectUtils the aspect utils
	 */
	public MetadataResultProducer(Map<String, Map<String,Object>> extensions,List<String> filename,AspectUtils aspectUtils) {

		this.extensionMap = extensions;
		this.filename = filename;
		this.aspectUtils=aspectUtils;

	}

	/**
	 * Instantiates a new metadata result producer.
	 *
	 * @param filename the filename
	 */
	public MetadataResultProducer(List<String> filename) {

		extensionMap = new HashMap<String, Map<String,Object>>();
		this.filename = filename;
	}

	/**
	 * Instantiates a new metadata result producer.
	 *
	 * @param obj the obj
	 * @param aspectUtils the aspect utils
	 */
	public MetadataResultProducer(CmisObject obj,AspectUtils aspectUtils) {

		this(new ArrayList<String>());
		cmisObject = obj;
		doc = (AlfrescoDocument) obj;
		filename.add(cmisObject.getName());
		this.aspectUtils=aspectUtils;
	}


	/* (non-Javadoc)
	 * @see org.netbeans.spi.wizard.WizardPage.WizardResultProducer#finish(java.util.Map)
	 */
	@SuppressWarnings("rawtypes")
	public Object finish(Map wizardData) throws WizardException {

		Map<String, Object> properties = null;

		for(String name : filename){

			properties = new HashMap<String,Object>();

			// Mise a jour des metadonnees
			if(cmisObject != null) {
				for(Property<?> prop : cmisObject.getProperties()){
					if(prop.getValue()!=null)
						properties.put(prop.getId(), prop.getValue());
				}
			}

			addGeneralMetadata(wizardData, properties, name);
			addCategories(wizardData, properties, name);

			if(cmisObject == null)
				addAspects(wizardData, properties, name);
			else{

				updateMetadata(wizardData, properties, name);
			}		

			extensionMap.put(name,properties);
		}

		return Summary.create(bundle.getString("finish_message"), null);

	}

	/**
	 * Update metadata.
	 *
	 * @param wizardData the wizard data
	 * @param properties the properties
	 * @param name the name
	 */
	@SuppressWarnings("rawtypes")
	private void updateMetadata(Map wizardData,Map<String,Object> properties,String name){

		String type = (String)properties.remove(PropertyIds.OBJECT_TYPE_ID);

		addAspects(wizardData,name,properties,cmisObject);

		if(properties.containsKey(PropertyIds.OBJECT_TYPE_ID)){
			type += ","+ properties.get(PropertyIds.OBJECT_TYPE_ID);
		}

		properties.put(PropertyIds.OBJECT_TYPE_ID,type);

		doc.updateProperties(properties);

	}

	/**
	 * Adds the aspects.
	 *
	 * @param wizardData the wizard data
	 * @param name the name
	 * @param properties the properties
	 * @param cmisObject the cmis object
	 */
	@SuppressWarnings("rawtypes")
	private void addAspects(Map wizardData,String name,Map<String,Object> properties,CmisObject cmisObject){

		StringBuilder id = new StringBuilder();

		for(String aspectName : aspectUtils.getAspectNames()){

			if(wizardData.containsKey(aspectName+name)){

				if(id.length()>0) id.append(",");
				id.append(aspectUtils.getID(aspectName));

				if(cmisObject != null){

					if(!doc.hasAspect(aspectUtils.getID(aspectName))){
						doc.addAspect(aspectUtils.getID(aspectName));
					}
				}

				for(Entry <String,PropertyDefinition<?>> prop : aspectUtils.getProperties(aspectName).entrySet()){

					if(prop.getValue().getPropertyType().equals(PropertyType.BOOLEAN)){

						Boolean value = (Boolean) wizardData.get(aspectName+prop.getValue().getDisplayName()+name);
						if(value != null)
							properties.put(prop.getValue().getId(), convertValue(prop.getValue().getPropertyType(),value));

					}
					else{
						String value = (String) wizardData.get(aspectName+prop.getValue().getDisplayName()+name);

						if(value != null && !value.isEmpty())
							properties.put(prop.getValue().getId(), convertValue(prop.getValue().getPropertyType(),value));
					}
				}
			}
		}

		properties.put(PropertyIds.OBJECT_TYPE_ID,id.toString());

	}

	/**
	 * Adds the aspects.
	 *
	 * @param wizardData the wizard data
	 * @param properties the properties
	 * @param name the name
	 */
	@SuppressWarnings("rawtypes")
	private void addAspects(Map wizardData,Map<String,Object> properties,String name){

		StringBuilder id = new StringBuilder();

		for(String aspectName : aspectUtils.getAspectNames()){

			if(wizardData.containsKey(aspectName+name)){

				if(id.length()>0) id.append(",");

				id.append(aspectUtils.getID(aspectName));

				for(Entry <String,PropertyDefinition<?>> prop : aspectUtils.getProperties(aspectName).entrySet()){


					if(prop.getValue().getPropertyType().equals(PropertyType.BOOLEAN)){

						Boolean value = (Boolean) wizardData.get(aspectName+prop.getValue().getDisplayName()+name);
						if(value != null)
							properties.put(prop.getValue().getId(), convertValue(prop.getValue().getPropertyType(),value));

					}
					else{
						String value = (String) wizardData.get(aspectName+prop.getValue().getDisplayName()+name);

						if(value != null && !value.isEmpty())
							properties.put(prop.getValue().getId(), convertValue(prop.getValue().getPropertyType(),value));
					}
				}
			}
		}

		if(id.length()!=0)
			properties.put(PropertyIds.OBJECT_TYPE_ID,id.toString());

	}

	/**
	 * Convert value.
	 *
	 * @param type the type
	 * @param value the value
	 * @return the object
	 */
	private Object convertValue(PropertyType type,Object value){

		switch (type) {

		case INTEGER:
			return Integer.parseInt((String)value);
		case DECIMAL:
			return Double.parseDouble((String)value);
		case DATETIME:
		
			try {
				Date date = SessionHandler.getDateFormat().parse((String) value);
				return ISO8601DateFormat.format(date);
				
			} catch (ParseException e) {
				e.printStackTrace();
			}	
		break;	
		
		case BOOLEAN:
			return value;
		
		case ID:
			
			//TODO
			return null;

		}


		return value;
	}

	/**
	 * Adds the general metadata.
	 *
	 * @param wizardData the wizard data
	 * @param properties the properties
	 * @param name the name
	 */
	@SuppressWarnings("rawtypes")
	private void addGeneralMetadata(Map wizardData,Map<String,Object> properties,String name){

		String title = (String) wizardData.get(TITLE_FIELD+name);
		String author = (String) wizardData.get(AUTHOR_FIELD+name);
		String description = (String) wizardData.get(DESCRIPTION_FIELD+name);	
		properties.put("cm:title",title);
		properties.put("cm:author",author);
		properties.put("cm:description",description);
	}

	/**
	 * Adds the categories.
	 *
	 * @param wizardData the wizard data
	 * @param properties the properties
	 * @param name the name
	 */
	@SuppressWarnings("rawtypes")
	private void addCategories(Map wizardData,Map<String,Object> properties,String name){

		String categories = (String) wizardData.get(CATEGORY_FIELD+name);

		if(categories.isEmpty()) return;

		List<String> allCategories = new ArrayList<String>();

		for(String cat : categories.split(ListCategoryPanel.CATEGORY_SEPARATOR)){
			allCategories.add(cat);
		}
		properties.put("cm:categories",allCategories);
	}

	/* (non-Javadoc)
	 * @see org.netbeans.spi.wizard.WizardPage.WizardResultProducer#cancel(java.util.Map)
	 */
	@SuppressWarnings("rawtypes")
	public boolean cancel(Map settings) {

		boolean dialogShouldClose = JOptionPane.showConfirmDialog (null, 
				bundle.getString("cancel_message")) == JOptionPane.OK_OPTION;
		return dialogShouldClose;
	}

}
